unit NDevices;

interface
uses Windows, NCore, NMedia, NUtils, Classes, SysUtils;

//-------------------------------------
// External functions
//-------------------------------------

procedure NDevicesGetInfo(var info: TNLibraryInfo);

//-------------------------------------
// Types
//-------------------------------------

type
 TFPScannerMan = class;
 TFPScanner = class;
 
 TScannerManCallback    = procedure(scannerId: string) of object;
 TScannerImageCallback  = procedure(scanner: TFPScanner; image: TNImage) of object;
 TScannerStatusCallback = procedure(scanner: TFPScanner) of object;

 TCamera = class(TObject)
  private
   _handle: Pointer;
   _cameraId: string;
   
   function  GetBoolParameter(paramId: LongWord): Boolean;
   procedure SetBoolParameter(paramId: LongWord; value: Boolean);

   constructor Create(pointerToCamera: Pointer); overload;
  public
   procedure CopyParameters(destination: TCamera);
   
   property  Id: string read _cameraId;

   property  Handle: Pointer read _handle;

   function  IsCapturing: Boolean;

   procedure StartCapturing;
   procedure StopCapturing;

   function  CurrentFrame: TNImage;

   function  VideoFormat: TCameraVideoFormat;
   procedure SetVideoFormat(var format: TCameraVideoFormat);
   function  GetVideoFormats: TArrayOfTCameraVideoFormat;

   procedure SetAutomaticSettings(value: Boolean);
   function  AutomaticSettings: Boolean;
   procedure SetMirrorHorizontal(value: Boolean);
   function  MirrorHorizontal: Boolean;
   procedure SetMirrorVertical(value: Boolean);
   function  MirrorVertical: Boolean;
 end;

 TCameraMan = class(TObject)
  private

  public
   constructor Create; overload;
   destructor  Destroy; override;

   function CameraCount: Integer;
   function GetCamera(index: Integer): TCamera; overload;
   function GetCamera(id: String): TCamera; overload;

 end;

 TFPScanner = class (TObject)
  private
   _handle: Pointer;
   _scannerId: string;

   _scannerImageCallback: array of TScannerImageCallback;
   _scannerFingerPlacedCallback: array of TScannerStatusCallback;
   _scannerFingerRemovedCallback: array of TScannerStatusCallback;

   constructor Create(scanner: Pointer);
  public
   destructor  Destroy(); override;

   function  IsCapturing(): Boolean;

   procedure StopCapturing;
   procedure StartCapturing(oneImage: Boolean = False);

   property  Handle: Pointer read _handle;

   property  Id: string read _scannerId;

   procedure SetScannedImageCallback(callback: TScannerImageCallback);
   procedure SetFingerRemovedCallback(callback: TScannerStatusCallback);
   procedure SetFingerPlacedCallback(callback: TScannerStatusCallback);
 end;

 TFPScannerMan = class(TObject)
  private
   _scannerAddedCallbacks: array of TScannerManCallback;
   _scannerRemovedCallbacks: array of TScannerManCallback;
  public
   class function GetAvailableScannerModules(): String;

   constructor Create; overload;
   constructor Create(scannerModules: String); overload;
   destructor  Destroy; override;

   function ScannerCount: Integer;

   function GetScanner(index: Integer): TFPScanner; overload;
   function GetScanner(id: String): TFPScanner; overload;

   procedure SetAddedScannerCallback(callback: TScannerManCallback);
   procedure SetRemovedScannerCallback(callback: TScannerManCallback);
 end;

implementation
//-------------------------------------
// Constants
//-------------------------------------

 const
  dllName = 'NDevices.dll';

  CAMERAP_MIRROR_HORIZONTAL  = 10200;
  CAMERAP_MIRROR_VERTICAL    = 10201;
  CAMERAP_AUTOMATIC_SETTINGS = 10301;

//-------------------------------------
// Types
//-------------------------------------
 type
  TCallBackScannerMan    = procedure(scannerId: Pchar;  Param: Pointer); stdcall;
  TCallBackScannerStatus = procedure(hScanner: Pointer; Param: Pointer); stdcall;
  TCallBackImageScanned  = procedure(hScanner: Pointer; hImage: Pointer; Param: Pointer); stdcall;


//-------------------------------------
// External(functions form dll)
//-------------------------------------

{$ifdef ver210}
function  NDevicesGetInfoInternal(var info: TNLibraryInfo): Integer stdcall; external dllName name 'NDevicesGetInfoW';
{$else}
function  NDevicesGetInfoInternal(var info: TNLibraryInfo): Integer stdcall; external dllName name 'NDevicesGetInfoA';
{$endif}

function  CameraManInitialize(): Integer stdcall; external dllName;
procedure CameraManUninitialize(); stdcall; external dllName;
function  CameraManGetCameraCount(var pValue: Integer): Integer stdcall; external dllName;
function  CameraManGetCamera(index: Integer; var hCamera: Pointer): Integer stdcall; external dllName;
{$ifdef ver210}
function  CameraManGetCameraById(szId: PChar; var hCamera: Pointer): Integer stdcall; external dllName name 'CameraManGetCameraByIdW';
{$else}
function  CameraManGetCameraById(szId: PChar; var hCamera: Pointer): Integer stdcall; external dllName name 'CameraManGetCameraByIdA';
{$endif}

{$ifdef ver210}
function  CameraGetId(hCamera: Pointer; pValue: PChar): Integer stdcall; external dllName name 'CameraGetIdW';
{$else}
function  CameraGetId(hCamera: Pointer; pValue: PChar): Integer stdcall; external dllName name 'CameraGetIdA';
{$endif}
function  CameraIsCapturing(hCamera: Pointer; var pValue: Integer): Integer stdcall; external dllName;
function  CameraStartCapturing(hCamera: Pointer): Integer stdcall; external dllName;
function  CameraStopCapturing(hCamera: Pointer): Integer stdcall; external dllName;
function  CameraGetCurrentFrame(hCamera: Pointer; var hImage: Pointer): Integer stdcall; external dllName;
function  CameraGetVideoFormats(hCamera: Pointer; pArVideoFormats: Pointer): Integer stdcall; external dllName;
function  CameraGetVideoFormat(hCamera: Pointer; var pVideoFormat: TCameraVideoFormat): Integer stdcall; external dllName;
function  CameraSetVideoFormat(hCamera: Pointer; var pVideoFormat: TCameraVideoFormat): Integer stdcall; external dllName;

{$ifdef ver210}
function  FPScannerGetId(hScanner: Pointer; pValue: PChar):  Integer stdcall; external dllName name 'FPScannerGetIdW';
{$else}
function  FPScannerGetId(hScanner: Pointer; pValue: PChar):  Integer stdcall; external dllName name 'FPScannerGetIdA';
{$endif}

function  FPScannerIsCapturing(hScanner: Pointer; var pValue: integer): Integer stdcall; external dllName;
function  FPScannerStopCapturing(hScanner: Pointer): Integer stdcall; external dllName;
function  FPScannerStartCapturingForOneImage(hScanner: Pointer): Integer stdcall; external dllName;
function  FPScannerStartCapturing(hScanner: Pointer): Integer stdcall; external dllName;
function  FPScannerSetFingerPlacedCallback(hScanner: Pointer; pCallback: TCallBackScannerStatus; pParam: Pointer): Integer stdcall; external dllName;
function  FPScannerSetFingerRemovedCallback(hScanner: Pointer; pCallback: TCallBackScannerStatus; pParam: Pointer): Integer stdcall; external dllName;
function  FPScannerSetImageScannedCallback(hScanner: Pointer; pCallback: TCallBackImageScanned; pParam: Pointer): Integer stdcall; external dllName;

function  FPScannerManInitialize: Integer stdcall; external dllName;
{$ifdef ver210}
function  FPScannerManInitializeWithModules(scannerModules: AnsiString): Integer stdcall; external dllName name 'FPScannerManInitializeWithModulesA'; overload;
function  FPScannerManInitializeWithModules(var scannerModules: Integer): Integer stdcall; external dllName name 'FPScannerManInitializeWithModulesW'; overload;
function  FPScannerManGetAvailableModules(var scannerModules: PChar): Integer stdcall; external dllName name 'FPScannerManGetAvailableModulesW';
{$else}
function  FPScannerManInitializeWithModules(scannerModules: AnsiString): Integer stdcall; external dllName name 'FPScannerManInitializeWithModulesA'; overload;
function  FPScannerManInitializeWithModules(var scannerModules: Integer): Integer stdcall; external dllName name 'FPScannerManInitializeWithModulesA'; overload;
function  FPScannerManGetAvailableModules(var scannerModules: PChar): Integer stdcall; external dllName name 'FPScannerManGetAvailableModulesA';
{$endif}
procedure FPScannerManUninitialize; stdcall; external dllName;

{$ifdef ver210}
function  FPScannerManSetScannerAddedCallback(pCallback: TCallBackScannerMan; pParam: Pointer): Integer stdcall; external dllName name 'FPScannerManSetScannerAddedCallbackW';
function  FPScannerManSetScannerRemovedCallback(pCallback: TCallBackScannerMan; pParam: Pointer): Integer stdcall; external dllName name 'FPScannerManSetScannerRemovedCallbackW';
function  FPScannerManGetScannerById(szID: PChar; var pValue: Pointer): Integer stdcall; external dllName name 'FPScannerManGetScannerByIdW';
{$else}
function  FPScannerManSetScannerAddedCallback(pCallback: TCallBackScannerMan; pParam: Pointer): Integer stdcall; external dllName name 'FPScannerManSetScannerAddedCallbackA';
function  FPScannerManSetScannerRemovedCallback(pCallback: TCallBackScannerMan; pParam: Pointer): Integer stdcall; external dllName name 'FPScannerManSetScannerRemovedCallbackA';
function  FPScannerManGetScannerById(szID: PChar; var pValue: Pointer): Integer stdcall; external dllName name 'FPScannerManGetScannerByIdA';
{$endif}
function  FPScannerManGetScannerCount(var pValue: LongInt): Integer stdcall; external dllName;
function  FPScannerManGetScanner(index: LongInt; var pValue: Pointer): Integer stdcall; external dllName;

//-----------------------------------------
// Misc
//-----------------------------------------

procedure NDevicesGetInfo(var info: TNLibraryInfo);
 var res: Integer;
begin
 res := NDevicesGetInfoInternal(info);
 NCheck(res);
end;

procedure ScannerAdded(scannerId: PChar; Param: Pointer); stdcall;
 var scannerMan: TFPScannerMan;
     i: Integer;
begin
 scannerMan := TFPScannerMan(param);
 if ((scannerMan <> nil) and (Length(scannerMan._scannerAddedCallbacks) <> 0)) then
 begin
   for i := 0 to Length(scannerMan._scannerAddedCallbacks) - 1 do
   begin
    scannerMan._scannerAddedCallbacks[i](string(scannerId));
   end;
 end;
end;

procedure ScannerRemoved(scannerId: PChar; Param: Pointer); stdcall;
var scannerMan: TFPScannerMan;
     i: Integer;
begin
 scannerMan := TFPScannerMan(param);
 if ((scannerMan <> nil) and (Length(scannerMan._scannerRemovedCallbacks) <> 0)) then
 begin
   for i := 0 to Length(scannerMan._scannerRemovedCallbacks) - 1 do
   begin
     scannerMan._scannerRemovedCallbacks[i](string(scannerId));
   end;  
 end;
end;

procedure FingerPlaced(hScanner: Pointer; Param: Pointer); stdcall;
 var i: Integer;
     scanner: TFPScanner;
begin
 scanner := TFPScanner(param);
  if (scanner <> nil) then
  begin
    for i := 0 to Length(scanner._scannerFingerPlacedCallback) - 1 do
    begin
     scanner._scannerFingerPlacedCallback[i](scanner);
    end;
  end;
end;

procedure FingerRemoved(hScanner: Pointer; Param: Pointer); stdcall;
 var i: Integer;
     scanner: TFPScanner;
begin
 scanner := TFPScanner(param);
  if (scanner <> nil) then
  begin
    for i := 0 to Length(scanner._scannerFingerRemovedCallback) - 1 do
    begin
     scanner._scannerFingerRemovedCallback[i](scanner);
    end;
  end;
end;

procedure FingerScanned(hScanner: Pointer; image: Pointer; Param: Pointer); stdcall;
 var imagePtr: TNImage;
     imageCloned: TNImage;
     scanner: TFPScanner;
     i: Integer;
begin
  scanner := TFPScanner(param);
  if (scanner <> nil) then
  begin
    imagePtr := TNImage.Create(image);
    imageCloned := imagePtr.Clone;
    for i := 0 to Length(scanner._scannerImageCallback) - 1 do
    begin
     scanner._scannerImageCallback[i](scanner, imageCloned);
    end;
    FreeAndNil(imageCloned);
  end;
end;

//-----------------------------------------
// TCameraMan class
//-----------------------------------------

constructor TCameraMan.Create;
 var ret: Integer;
begin
 inherited Create;
 ret := CameraManInitialize;
 NCheck(ret);
end;

destructor TCameraMan.Destroy;
begin
 CameraManUninitialize;
end;

function TCameraMan.CameraCount: Integer;
 var ret, value: Integer;
begin
 value := 0;
 ret := CameraManGetCameraCount(value);
 NCheck(ret);
 Result := value;
end;

function TCameraMan.GetCamera(index: Integer): TCamera;
 var ret: Integer;
     hCamera: Pointer;
begin
 hCamera := nil;
 ret := CameraManGetCamera(index, hCamera);
 NCheck(ret);
 Result := TCamera.Create(hCamera);
end;

function TCameraMan.GetCamera(id: String): TCamera;
 var ret: Integer;
     hCamera: Pointer;
begin
 hCamera := nil;
 ret := CameraManGetCameraById(PChar(id), hCamera);
 NCheck(ret);
 Result := TCamera.Create(hCamera);
end;

//-----------------------------------------
// TCamera class
//-----------------------------------------
constructor TCamera.Create(pointerToCamera: Pointer);
 var res: Integer;
     tmp: PChar;
begin
 inherited Create;
 _handle := pointerToCamera;

 _cameraId := '';
 res := CameraGetId(Handle, nil);
 NCheck(res);
 GetMem(tmp, (res + 1) * SizeOf(PChar));
 res := CameraGetId(Handle, tmp);
 NCheck(res);
 _cameraId := String(tmp);
 FreeMem(tmp);

end;

function TCamera.GetBoolParameter(paramId: LongWord): Boolean;
 var res: Boolean;
begin
 GetParameter(Handle, paramId, res);
 Result := res;
end;

procedure TCamera.SetBoolParameter(paramId: LongWord; value: Boolean);
begin
 SetParameter(Handle, paramId, value);
end;

procedure TCamera.CopyParameters(destination: TCamera);
begin
 NObjectCopyParameters(destination.Handle, Handle);
end;

function TCamera.IsCapturing: boolean;
 var res, value: Integer;
begin
 Result := false;
 res := CameraIsCapturing(Handle, value);
 NCheck(res);
 if (value = 1) then Result := true;
end;

procedure TCamera.StartCapturing;
 var res: Integer;
begin
 res := CameraStartCapturing(Handle);
 NCheck(res);
end;

procedure TCamera.StopCapturing;
 var res: Integer;
begin
 res := CameraStopCapturing(Handle);
 NCheck(res);
end;

function TCamera.CurrentFrame: TNImage;
 var res: Integer;
     image: Pointer;
begin
 image := nil;
 res := CameraGetCurrentFrame(Handle, image);
 NCheck(res);
 Result := TNImage.Create(image);
end;

function TCamera.MirrorVertical: Boolean;
begin
 Result := GetBoolParameter(CAMERAP_MIRROR_VERTICAL);
end;

function TCamera.MirrorHorizontal: Boolean;
begin
 Result := GetBoolParameter(CAMERAP_MIRROR_HORIZONTAL);
end;

function TCamera.AutomaticSettings: Boolean;
begin
 Result := GetBoolParameter(CAMERAP_AUTOMATIC_SETTINGS);
end;

procedure TCamera.SetAutomaticSettings(value: Boolean);
begin
 SetBoolParameter(CAMERAP_AUTOMATIC_SETTINGS, value);
end;

procedure TCamera.SetMirrorHorizontal(value: Boolean);
begin
 SetBoolParameter(CAMERAP_MIRROR_HORIZONTAL, value);
end;

procedure TCamera.SetMirrorVertical(value: Boolean);
begin
 SetBoolParameter(CAMERAP_MIRROR_VERTICAL, value);
end;

function TCamera.VideoFormat: TCameraVideoFormat;
 var res: Integer;
     output: TCameraVideoFormat;
begin
 res := CameraGetVideoFormat(Handle, output);
 NCheck(res);
 Result := output;
end;

procedure TCamera.SetVideoFormat(var format: TCameraVideoFormat);
 var res: Integer;
begin
 res := CameraSetVideoFormat(Handle, format);
 NCheck(res);
end;

function TCamera.GetVideoFormats: TArrayOfTCameraVideoFormat;
 var res: Integer;
     formats: TArrayOfTCameraVideoFormat;
begin
  res := CameraGetVideoFormats(Handle, nil);
  NCheck(res);
  SetLength(formats, res);
  if (res > 0) then
  begin
    res := CameraGetVideoFormats(Handle, @(formats[0]));
    NCheck(res);
  end;
  Result := formats;
end;

//-------------------------------------
// TFPScanner class
//-------------------------------------

constructor TFPScanner.Create(Scanner: Pointer);
var res: Integer;
    tmp: PChar;
 begin
   inherited Create;
   self._handle := Scanner;

  _scannerId := '';
  res := FPScannerGetId(Handle, nil);
  NCheck(res);
  GetMem(tmp, (res + 1) * SizeOf(PChar));
  res := FPScannerGetId(Handle, tmp);
  NCheck(res);
  _scannerId := String(tmp);
  FreeMem(tmp);

  res :=  FPScannerSetImageScannedCallback(Handle, FingerScanned, Self);
  NCheck(res);

  res :=  FPScannerSetFingerPlacedCallback(Handle, FingerPlaced, Self);
  NCheck(res);

  res :=  FPScannerSetFingerRemovedCallback(Handle, FingerRemoved, Self);
  NCheck(res);

  SetLength(_scannerImageCallback, 0);
  SetLength(_scannerFingerPlacedCallback, 0);
  SetLength(_scannerFingerRemovedCallback, 0);
 end;

 destructor TFPScanner.Destroy;
 begin
  inherited Destroy;
  SetLength(_scannerImageCallback, 0);
  SetLength(_scannerFingerPlacedCallback, 0);
  SetLength(_scannerFingerRemovedCallback, 0);
 end;

 procedure TFPScanner.SetFingerPlacedCallback(callback: TScannerStatusCallback);
  var i: Integer;
 begin
  i := Length(_scannerFingerPlacedCallback);
  SetLength(_scannerFingerPlacedCallback, i + 1);
  _scannerFingerPlacedCallback[i] := callback;
 end;

 procedure TFPScanner.SetFingerRemovedCallback(callback: TScannerStatusCallback);
  var i: Integer;
 begin
  i := Length(_scannerFingerRemovedCallback);
  SetLength(_scannerFingerRemovedCallback, i + 1);
  _scannerFingerRemovedCallback[i] := callback;
 end;

 procedure TFPScanner.SetScannedImageCallback(callback: TScannerImageCallback);
  var i: Integer;
 begin
  i := Length(_scannerImageCallback);
  SetLength(_scannerImageCallback, i + 1);
  _scannerImageCallback[i] := callback;
 end;

 procedure TFPScanner.StopCapturing;
  var res: Integer;
 begin
  res := FPScannerStopCapturing(Handle);
  NCheck(res);
 end;

 function TFPScanner.IsCapturing():boolean;
  var res: Integer;
      ret: Integer;
 begin
  res := FPScannerIsCapturing(Handle, ret);
  NCheck(res);
  if (ret = 1) then IsCapturing := true else IsCapturing := false;
 end;

 procedure TFPScanner.StartCapturing(oneImage: Boolean = False);
  var res: Integer;
 begin
  if (oneImage) then
    res := FPScannerStartCapturingForOneImage(Handle)
  else
    res := FPScannerStartCapturing(Handle);
  NCheck(res);
 end;

//-------------------------------------
// TFPScannerMan class
//-------------------------------------

class function TFPScannerMan.GetAvailableScannerModules(): String;
    var res: Integer;
        str: PChar;
begin
  res :=  FPScannerManGetAvailableModules(str);
  NCheck(res);
  Result := String(str);
  NFree(str);
end;

constructor TFPScannerMan.Create;
 var ret: Integer;
begin
 inherited Create;
 ret := FPScannerManInitializeWithModules(AnsiString(GetAvailableScannerModules));
 NCheck(ret);
 ret :=  FPScannerManSetScannerAddedCallback(ScannerAdded, Self);
 NCheck(ret);
 ret :=  FPScannerManSetScannerRemovedCallback(ScannerRemoved, Self);
 NCheck(ret);
 SetLength(_scannerAddedCallbacks, 0);
 SetLength(_scannerRemovedCallbacks, 0);
end;

constructor TFPScannerMan.Create(scannerModules: String);
 var ret: Integer;
     temp: Integer;
begin
 inherited Create;
 temp := 0;
 if (Length(scannerModules) = 0) then
  ret := FPScannerManInitializeWithModules(temp)
 else
  ret := FPScannerManInitializeWithModules(AnsiString(scannerModules));
 NCheck(ret);
 ret :=  FPScannerManSetScannerAddedCallback(ScannerAdded, Self);
 NCheck(ret);
 ret :=  FPScannerManSetScannerRemovedCallback(ScannerRemoved, Self);
 NCheck(ret);
 SetLength(_scannerAddedCallbacks, 0);
 SetLength(_scannerRemovedCallbacks, 0);
end;

destructor TFPScannerMan.Destroy;
begin
 FPScannerManUninitialize;
 SetLength(_scannerAddedCallbacks, 0);
 SetLength(_scannerRemovedCallbacks, 0);
end;

function TFPScannerMan.ScannerCount: Integer;
 var ret, value: Integer;
begin
 value := 0;
 ret := FPScannerManGetScannerCount(value);
 NCheck(ret);
 Result := value;
end;

function TFPScannerMan.GetScanner(index: Integer): TFPScanner;
 var ret: Integer;
     Handle: Pointer;
begin
 ret := FPScannerManGetScanner(index, Handle);
 NCheck(ret);
 Result := TFPScanner.Create(Handle);
end;

function TFPScannerMan.GetScanner(Id: string): TFPScanner;
 var ret: Integer;
     Handle: Pointer;
begin
 ret := FPScannerManGetScannerById(PChar(id), Handle);
 NCheck(ret);
 Result := TFPScanner.Create(Handle);
end;

procedure TFPScannerMan.SetAddedScannerCallback(callback: TScannerManCallback);
 var i: Integer;
begin
  i := Length(_scannerAddedCallbacks);
  SetLength(_scannerAddedCallbacks, i + 1);
  _scannerAddedCallbacks[i] := callback;
end;

 procedure TFPScannerMan.SetRemovedScannerCallback(callback: TScannerManCallback);
  var i: Integer;
 begin
  i := Length(_scannerRemovedCallbacks);
  SetLength(_scannerRemovedCallbacks, i + 1);
  _scannerRemovedCallbacks[i] := callback;
 end;

end.
